#include "pch.h"
#include "GDxImgui.h"
#include <IMGUI/imgui.h>
#include <IMGUI/imgui_impl_win32.h>
#include <IMGUI/imgui_impl_dx11.h>
#include <IMGUI/ImGuizmo.h>
#include "GDxGpuProfiler.h"


void GDxImgui::Init(HWND hwnd, ID3D11Device* device, ID3D11DeviceContext* deviceContext)
{
	IMGUI_CHECKVERSION();
	ImGui::CreateContext();
	ImGuiIO& io = ImGui::GetIO(); (void)io;
	io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;       // Enable Keyboard Controls
	//io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;      // Enable Gamepad Controls
	//io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;           // Enable Docking
	//io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;         // Enable Multi-Viewport / Platform Windows
	//io.ConfigViewportsNoAutoMerge = true;
	//io.ConfigViewportsNoTaskBarIcon = true;
	//io.ConfigViewportsNoDefaultParent = true;
	//io.ConfigDockingAlwaysTabBar = true;
	//io.ConfigDockingTransparentPayload = true;
#if 0
	io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleFonts;     // FIXME-DPI: THIS CURRENTLY DOESN'T WORK AS EXPECTED. DON'T USE IN USER APP!
	io.ConfigFlags |= ImGuiConfigFlags_DpiEnableScaleViewports; // FIXME-DPI
#endif
	//io.WantCaptureMouse = true;

	 // Setup Dear ImGui style
	ImGui::StyleColorsDark();

	// When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones.
// 	ImGuiStyle& style = ImGui::GetStyle();
// 	if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
// 	{
// 		style.WindowRounding = 0.0f;
// 		style.Colors[ImGuiCol_WindowBg].w = 1.0f;
// 	}

	// Setup Platform/Renderer bindings
	ImGui_ImplWin32_Init(hwnd);
	ImGui_ImplDX11_Init(device, deviceContext);
}

void GDxImgui::BeginFrame()
{
	ImGui_ImplDX11_NewFrame();
	ImGui_ImplWin32_NewFrame();
	ImGui::NewFrame();
	ImGuizmo::BeginFrame();
}

void GDxImgui::SetGUIContent(bool bShowGizmo, const float* cameraView, float* cameraProjection, float* objectLocation, float* objectRotation, float* objectScale, float& cameraSpeed, int clientWidth, int clientHeight)
{
	{
		ImGui::Begin("Profiler");

		ImGui::Text("Viewport width : %d, height : %d", clientWidth, clientHeight);
		ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);

		std::vector<float> passRenderTimePercentage;
		double allPassTime = GDxGpuProfiler::Get().GetAllPassTime();
		for (auto profile : GDxGpuProfiler::Get().GetProfileDatas())
		{
			ImGui::Text("%s : %.3f ms", profile.name.data(), profile.time);
			//passRenderTimePercentage.push_back(profile.time / (1000.0f / ImGui::GetIO().Framerate));
			passRenderTimePercentage.push_back(profile.time / (allPassTime));
		}
		static float values[90] = { 0 };
		static int values_offset = 0;
		static double refresh_time = 0.0;

		if (refresh_time == 0.0)
			refresh_time = ImGui::GetTime();

		while (refresh_time < ImGui::GetTime()) // Create data at fixed 60 hz rate
		{
			values[values_offset] = 1000.0f / ImGui::GetIO().Framerate;
			values_offset = (values_offset + 1) % IM_ARRAYSIZE(values);
			refresh_time += 1.0f / 60.0f;
		}

		float maxTime = 0.0f;
		for (auto i = 0; i < IM_ARRAYSIZE(values); i++)
		{
			if (values[i] > maxTime)
				maxTime = values[i];
		}

		float ind = ceil(log2(maxTime / 17.0f));
		ImGui::PlotLines("", values, IM_ARRAYSIZE(values), values_offset, "Framerate", 0.0f, 17.0f * pow(2.0f, max(ind, 0.0f)), ImVec2(300, 80));

		ImGui::PlotHistogram("", passRenderTimePercentage.data(), passRenderTimePercentage.size(), 0, "Pass", 0.0f, 1.0f, ImVec2(300, 80));
		ImGui::End();
	}
	{
		ImGui::Begin("Manipulation");
		Manipulation(bShowGizmo, cameraView, cameraProjection, objectLocation, objectRotation, objectScale, cameraSpeed);
		ImGui::End();
	}
}

void GDxImgui::Render()
{
	ImGui::Render();

	ImGuiIO& io = ImGui::GetIO();

	ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
// 	if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
// 	{
// 		ImGui::UpdatePlatformWindows();
// 		ImGui::RenderPlatformWindowsDefault();
// 	}
}

void GDxImgui::ShutDown()
{
	ImGui_ImplDX11_Shutdown();
	ImGui_ImplWin32_Shutdown();
	ImGui::DestroyContext();
}

void GDxImgui::Manipulation(bool bShowGizmo, const float* cameraView, float* cameraProjection, float* objectLocation, float* objectRotation, float* objectScale, float& cameraSpeed)
{
	ImGuiIO& io = ImGui::GetIO();

	static bool bSelectMode = false;
	static ImGuizmo::OPERATION mCurrentGizmoOperation(ImGuizmo::TRANSLATE);
	static ImGuizmo::MODE mCurrentGizmoMode(ImGuizmo::LOCAL);
	static bool useSnap = false;
	static float snap[3] = { 1.f, 1.f, 1.f };
	static ImU8 initCameraSpeedExp = (mCameraSpeedUpperBound - mCameraSpeedLowerBound) / 2 + 1;
	static ImU8 cameraSpeedExp = initCameraSpeedExp;

	if (ImGui::IsKeyPressed(81) && !ImGui::IsMouseDown(1)) // Q Key
	{
		bSelectMode = true;
	}
	if (ImGui::IsKeyPressed(87) && !ImGui::IsMouseDown(1)) // W Key
	{
		mCurrentGizmoOperation = ImGuizmo::TRANSLATE;
		bSelectMode = false;
	}
	if (ImGui::IsKeyPressed(69) && !ImGui::IsMouseDown(1)) // E Key
	{
		mCurrentGizmoOperation = ImGuizmo::ROTATE;
		bSelectMode = false;
	}
	if (ImGui::IsKeyPressed(82) && !ImGui::IsMouseDown(1)) // R Key
	{
		mCurrentGizmoOperation = ImGuizmo::SCALE;
		bSelectMode = false;
	}

	if (ImGui::IsMouseDown(1))
		cameraSpeedExp += (ImU8)io.MouseWheel;
	if (cameraSpeedExp > mCameraSpeedUpperBound)
		cameraSpeedExp = mCameraSpeedUpperBound;
	if (cameraSpeedExp < mCameraSpeedLowerBound)
		cameraSpeedExp = mCameraSpeedLowerBound;
	ImGui::SliderScalar("CameraSpeed", ImGuiDataType_U8, &cameraSpeedExp, &mCameraSpeedLowerBound, &mCameraSpeedUpperBound, "%u");
	cameraSpeed = mInitialCameraSpeed * (float)pow(1.5f, cameraSpeedExp - initCameraSpeedExp);

	if (ImGui::RadioButton("Translate", mCurrentGizmoOperation == ImGuizmo::TRANSLATE))
		mCurrentGizmoOperation = ImGuizmo::TRANSLATE;
	ImGui::SameLine();
	if (ImGui::RadioButton("Rotate", mCurrentGizmoOperation == ImGuizmo::ROTATE))
		mCurrentGizmoOperation = ImGuizmo::ROTATE;
	ImGui::SameLine();
	if (ImGui::RadioButton("Scale", mCurrentGizmoOperation == ImGuizmo::SCALE))
		mCurrentGizmoOperation = ImGuizmo::SCALE;

	if (mCurrentGizmoOperation != ImGuizmo::SCALE)
	{
		if (ImGui::RadioButton("Local", mCurrentGizmoMode == ImGuizmo::LOCAL))
			mCurrentGizmoMode = ImGuizmo::LOCAL;
		ImGui::SameLine();
		if (ImGui::RadioButton("World", mCurrentGizmoMode == ImGuizmo::WORLD))
			mCurrentGizmoMode = ImGuizmo::WORLD;
	}
	/*
	if (ImGui::IsKeyPressed(83))
		useSnap = !useSnap;
	*/
	ImGui::Checkbox("", &useSnap);
	ImGui::SameLine();

	switch (mCurrentGizmoOperation)
	{
	case ImGuizmo::TRANSLATE:
		ImGui::InputFloat3("Snap", &snap[0]);
		break;
	case ImGuizmo::ROTATE:
		ImGui::InputFloat("Angle Snap", &snap[0]);
		break;
	case ImGuizmo::SCALE:
		ImGui::InputFloat("Scale Snap", &snap[0]);
		break;
	}

	float matrix[16];
	ImGuizmo::RecomposeMatrixFromComponents(objectLocation, objectRotation, objectScale, matrix);

	ImGuizmo::SetRect(0, 0, io.DisplaySize.x, io.DisplaySize.y);
	if (!bSelectMode && bShowGizmo)
		ImGuizmo::Manipulate(cameraView, cameraProjection, mCurrentGizmoOperation, mCurrentGizmoMode, matrix, NULL, useSnap ? &snap[0] : NULL, NULL, NULL);

	ImGuizmo::DecomposeMatrixToComponents(matrix, objectLocation, objectRotation, objectScale);
}


